//--------------------------------------------------------------------
// Microsoft OLE DB Test
//
// Copyright 1995-2000 Microsoft Corporation.  
//
// @doc 
//
// @module CTree Header Module | This module contains header information for CTree
//
// @comm
// Special Notes...:	(OPTIONAL NOTES FOR SPECIAL CIRCUMSTANCES)
//
// <nl><nl>
// Revision History:<nl>
//---------------------------------------------------------------------------

#ifndef _CTree_HPP_
#define _CTree_HPP_

/////////////////////////////////////////////////////////////////////
// Includes
//
/////////////////////////////////////////////////////////////////////
#include "List.h"


typedef enum
{
	NC_NONE			= 0x0,
	NC_Collection	= 0x1,
	NC_Singleton	= 0x2,
	NC_LEAF			= 0x4,
	NC_SUBTREE		= 0x8,
	NC_LEAF_Collection	= NC_LEAF | NC_Collection,
} NODE_CONSTRAINT;

typedef enum
{
	PCO_NONE		= 0x0,
	PCO_DIFF		= 0x1,
	PCO_OVERLAP		= 0x2,
} PAIR_CONSTRAINT;

typedef BOOL (*CColApplicator)(CCol*);

class CSchema
{
private:	 

	// row URL (the row on whose children rowset is opened)
	WCHAR *		m_pwszRowURL;
	// the last part of the URL (name relative to parent)
	WCHAR *		m_pwszRowName;

	// parent row seed; equals to -1 if not used  => seed values limited to 
	// half of MAX_PTR
	// for an ini file this value should be the row number
	LONG_PTR	m_lSeed;
	
	// Number of leaves this schema has
	ULONG_PTR	m_cLeaves;

	// info about the row being collection or not
	BOOL		m_fIsCollection;

protected:
	// Indicates if NULL values will be generated by MakeData. 
	ENULL 		m_eNull; 

	// Column which has index on it. <nl>
	DBORDINAL 	m_ulIndex;
	
	// Column which has primary key constraint on it. <nl>
	DBORDINAL 	m_ulPrimaryKey;

	// @cmember Indicates if Primary Key will be created on a table
	ECREATE_PK	m_eCreatePK;

	// List of columns in this schema. <nl>
	CList <CCol,CCol&> 			m_ColList;

	// List of descendents of this row. <nl>
	CList <CSchema*,CSchema*> 	m_ChildrenList;

	// the parent CSchema
	CSchema	*					m_pParentSchema;

	// Count of supported variant subtypes
	ULONG	m_cVariantSubTypes;

	// Array of variant subtypes
	DBTYPE	m_rgVariantSubTypes[MAX_VARIANT_SUBTYPES];

	// Updates CCol List based on information found in rgData. rgData
	// an entire row.
	HRESULT UpdateCCol
	(
		DBCOUNTITEM		cBindings,
		DBBINDING *		rgBindings,
		void *			pData,
		CCol &			NewCol,				// [IN/OUT] Element from the CCol List
		EDATATYPES		eDataTypes,			// [IN] Enum for column data type
		ULONG *			pulAutoIncPrec,		// [IN/OUT] Precision of largest autoincrement column
		LONG_PTR		lSQLSupport			// [IN] SQL Support Level
	);

public:	
	
	// used to speed up node retrieval
	// when moving to the next node in a tree, based on a CSchema ptr
	// (the crt position), one will get the parent CSchema and 
	// try to move child (m_ulCacheSelection+1)
	LONG_PTR	m_lCacheSelection;

	CSchema
	(
		CSchema	*	pParentSchema = NULL,	// [in] the parent schema of the row
		WCHAR	*	pwszParentRowURL = NULL,// [in] the URL of the parent row
		LONG_PTR	lSeed = -1,				// [in] the seed value used for the row
		ENULL		eNull = USENULLS,		// [in] tells if nulls are used
		ECREATE_PK	eCreatePK=CREATENEVER_PK	// [in] tells if a primary key be created
	);	
	
	CSchema::CSchema
	(
		CSchema *	pSchema,				// [in] the schema to be copied
		CSchema	*	pParentSchema	= NULL,	// [in] the parent schema (if exists)
		BOOL		fNonRecursive	= FALSE	// [in] whether the copying is recursive or not
	);

	virtual ~CSchema(void);

	void	SetParentSchema(CSchema *pSchema)
	{
		m_pParentSchema = pSchema;
	}

	CSchema	*GetParentSchema() 
	{
		return m_pParentSchema;
	}

	// manipulate the list of children
	void	AddChild(CSchema *pSchema);
	void	RemoveChild(CSchema *pSchema);
	
	virtual HRESULT InitSchema(IUnknown* pIUnknown);
	
	virtual HRESULT SetFromColumnsInfo
	(
		IColumnsInfo *	pIColumnsInfo,
		DBORDINAL *		pcColsColInfo
	);

	virtual HRESULT SetFromColumnsRowset
	(
		IColumnsRowset * pIColumnsRowset,
		DBORDINAL * pcColsFound
	);

	// retrieve the column list (for the copy constructor)
	CList <CCol,CCol&> 	*GetColList()
	{
		return &m_ColList;
	}

	// retrieve the children list (for the copy constructor)
	CList <CSchema*,CSchema*> 	*GetChildrenList()
	{
		return &m_ChildrenList;
	}

	// @cmember Gets CCol object associated with criteria
  	virtual HRESULT	GetColInfo
	(
		CColApplicator	pfApp, 	// [IN]  Accepting criteria
		CCol & 	ColInfo			// [OUT] CCol object 
	);

	// @cmember Gets CCol object associated with column number.
  	virtual HRESULT	GetColInfo
	(
		DBORDINAL 	ulColNum, 	// [IN]  Column number 
		CCol & 	ColInfo			// [OUT] CCol object 
	);

	// @cmember Gets first CCol object associated with Provider type.
	virtual HRESULT	GetColInfo
	(
		CCol & 		col,		// [OUT] CCol object 
		DBTYPE 		wType		// [IN]  Datatype
	);

	// @cmember Gets first CCol object associated with Provider type.
	virtual HRESULT	GetColInfo
	(
		CCol & 		col,			// [OUT] CCol object 
		WCHAR		*pwszTypeName	// [IN]  Provider typename
	);

	// @cmember Gets first CCol object associated with dbcid (ColumnID).
	virtual HRESULT	GetColInfo
	(
		DBID *		dbcid,		// [IN]  Column ID to be retrieved
		CCol & 		col			// [OUT] CCol object 
	);

	// @cmember Returns CCol object associated with column number
	virtual CCol &	GetColInfoForUpdate
	(
		DBORDINAL ulColNum		// [IN]  Column number searching for, 1 based
	);

	LONG_PTR		GetSeed() 
	{
		return m_lSeed;
	}

	// @cmember returns info about the row being collection or not
	BOOL			IsCollection()
	{
		return m_fIsCollection;
	}

	virtual HRESULT MakeData
	(
		WCHAR * 	wszData,				// @parm [OUT] Return data
		DBCOUNTITEM	ulRowNum,				// @parm [IN]  Row number which is one based
		DBORDINAL	iOrdinal,				// @parm [IN]  Column number which is one based
		EVALUE 		eValue,					// @parm [IN]  Primary or secondary data 
		DBTYPE		wSubType=DBTYPE_EMPTY,	// @parm [IN]  Column SubType
		BOOL		bNoNullValues=FALSE,	// [IN] if bNoNullValues is True then MakeData will return valid data.
		DBTYPE *	pwVariantType=NULL		// [OUT] Optional, returns underlying type if backend is a Variant
	);

	// Returns client's request to use nulls or not use nulls.
	inline ENULL GetNull(void)
	{
		return m_eNull;
	}

	// Returns client's request to use nulls or not use nulls.
	inline void SetNull(ENULL eNull)
	{
		m_eNull = eNull;
	}

	// Return Column number which index is created on.
	inline DBORDINAL GetIndexColumn(void)
	{
		return m_ulIndex;
	}

	// Set Column number which index is created on.
	inline DBORDINAL SetIndexColumn(DBORDINAL ulIndex)
	{
		DBORDINAL	ulOldIndex = m_ulIndex;
		
		m_ulIndex=ulIndex;
		return ulOldIndex;
	}

	// Returns Column number which primary key is created on.
	inline DBORDINAL GetPrimaryKeyColumn(void) {return m_ulPrimaryKey;}

	// Sets Column number which Primary key is created on.
	// Note!!!: if m_eCreatePK==CREATENEVER_PK then m_ulPrimaryKey will be ignored
	inline void SetPrimaryKeyColumn(DBORDINAL ulPrimaryKey) {m_ulPrimaryKey=ulPrimaryKey;}

	// Returns m_eCreatePK
	inline ECREATE_PK GetCreatePK(void) {return m_eCreatePK;}

	//	Sets m_eCreatePK
	void SetCreatePK(ECREATE_PK eCreatePK)
	{
		m_eCreatePK=eCreatePK;
		if (CREATENEVER_PK==eCreatePK)
			m_ulPrimaryKey=0;
	}

	DBCOUNTITEM	SetLeafNo(DBCOUNTITEM cLeaves)
	{
		return m_cLeaves = cLeaves;
	}

	LONG_PTR	SetSeed(LONG_PTR lSeed) 
	{
		return m_lSeed = lSeed;
	}

 	DBCOUNTITEM	GetLeafNo(void)
	{
		return m_cLeaves;
	}

	DBCOUNTITEM	GetChildrenNo(void)
	{
		return m_ChildrenList.GetCount();
	}

	// returns the URL associated with this row
	WCHAR *	GetRowURL() 
	{
		return m_pwszRowURL;
	}

	// returns the last part of the name (e.g. confprov://dso/session/t/1/2/3 => 3)
	WCHAR *	GetRowName() 
	{
		return m_pwszRowName;
	}

	WCHAR *	SetRowURL
	(
		WCHAR *pwszRowURL
	);

	// methods to retrieve one of the children
	CSchema * GetChild
	(
		DBCOUNTITEM	ulIndex
	);

	CSchema * GetChild
	(
		WCHAR *			pwszName,
		DBCOUNTITEM *	pulIndex = NULL
	);

	// method to update the URL of the tree
	void UpdateURL(WCHAR *pwszURL);

	// returns the index of the child given in the children list
	LONG_PTR GetChildIndex(CSchema *pSchema);

	// Populates an col list with a Provider's type information
	HRESULT	CSchema::PopulateTypeInfo
	(
		IUnknown *	pIUnknown,
		LONG_PTR	lSQLSupport
	);

	// Returns the number of columns in the schema list
	inline DBORDINAL CountColumnsOnSchema(void)
	{
		return  (DBORDINAL) m_ColList.GetCount(); 
	};

	inline void GetVariantSubTypes(ULONG * pcVariantSubTypes, DBTYPE ** pprgVariantSubTypes)
	{
		ASSERT(pcVariantSubTypes && pprgVariantSubTypes);

		*pcVariantSubTypes		= m_cVariantSubTypes;
		*pprgVariantSubTypes	= m_rgVariantSubTypes;
	}
};


class CTree
{
protected:	 

	// A Tree to CSchema objects
	CSchema	*			m_pRootSchema;
	CSchema	*			m_pCrtSchema;
	// Iteration limit
	// This is the level of the subtree root.
	// SetPosition sets this value and ResetPosition resets it to 0.
	// When the iteration returns to this level in the tree, nav will stop.
	CSchema	*			m_pStartingSchema;


	// The depth that the tree was created with
	DBCOUNTITEM			m_ulMaxDepth;

	// Number of children per node when tree was created
	DBCOUNTITEM			m_cMaxChildrenPerNode;

	// Root Node
	WCHAR *				m_pwszTreeRoot;

	// RootBinder interfaces
	IBindResource *		m_pIBindResource;
	ICreateRow *		m_pICreateRow;

	// store the value of DBPROP_GENERATEURL property
	LONG_PTR			m_lGenerateURL;

public:	

	CTree
	(
		IUnknown *	pSessionIUnknown,		// [IN] SessionInterface
		WCHAR *		pwszModuleName=NULL,	// [IN] Tree name, optional (Default=NULL)
		ENULL		eNull=USENULLS			// [IN] Should nulls be used (Default=USENULLS)
	);	
	
	virtual ~CTree(void);

	virtual HRESULT CreateTree
	(
		WCHAR *		pwszRootURL,
		DBCOUNTITEM ulDepth, 
		DBCOUNTITEM cMaxChildrenPerNode
	);

	virtual HRESULT	CopyTree
	(
		CTree *	pCTree,			// [in] the tree to be copied
		WCHAR *	pwszBaseRootURL	// [in] the base root URL where to copy the tree
	);

	virtual HRESULT	CreateAndPopulateRow
	(
		CSchema	*		pParentSchema,	// [in] parent node's schema 
		ULONG_PTR		ulSeed,			// [in] the seed of the row
		WCHAR *			pwszURL,		// [in] row URL
		DBBINDURLFLAG	dwBindURLFlags,	// [in] binding flags
		CSchema	**		ppSchema,		// [out] new row's schema 
		IUnknown **		ppIRowChange,	// [out] pointer to interface to be returned
		WCHAR **		ppwszNewURL		// [out] pointer to the name of the new row
	);

	// check the rowset opened on pwszParentURL
	// making sure that all the rows expected are contained and their values are ok
	BOOL CheckRowset
	(
		IRowset	*	pIRowset,			// [in] interface on the checked rowset
		WCHAR *		pwszParentURL,		// [in] the URL used to open the rowset
		BOOL		fCmpRowVal = TRUE	// [in] whether or not to compare the row values
	);

	BOOL CheckRowset
	(
		IRowset	*	pIRowset,		// [in] interface on the checked rowset
		CSchema	*	pRowsetSchema,	// [in] schema of the rowset to be checked
		BOOL		fCmpRowVal		// [in] whether or not to compare the row values
	);

	// checks whether all the rows in the tree can be bound
	BOOL CheckTreeStructure();

	// checks the tree structure as well as the values
	BOOL CheckTree();

	virtual	HRESULT	CreateChildRowset
	(	
		CSchema	*	pParentSchema,
		WCHAR *		pwszParentURL, 
		DBCOUNTITEM	cChildren,
		DBCOUNTITEM	ulDepth,
		ULONG_PTR	ulParentSeed = 1
	);

	virtual HRESULT	DestroyTree();

	virtual CSchema * GetSchema(WCHAR *pwszURL);
	
	DBCOUNTITEM	GetMaxDepth()
	{
		return m_ulMaxDepth;
	}

	DBCOUNTITEM			GetMaxChildrenPerNode()
	{
		return m_cMaxChildrenPerNode;
	}

	virtual CSchema * GetCurrentSchema();

	virtual WCHAR * GetCurrentRowURL();

	virtual CSchema*	GetRootSchema();

	virtual	HRESULT	GetRelativeRowURL
	(
		WCHAR *		pwszAbsoluteRowURL,	// [in]
		WCHAR **	ppwszRelativeRowURL	// [out]
	);

	virtual HRESULT	GetAbsoluteRowURL
	(
		WCHAR *		pwszRelativeRowURL,	// [in]
		WCHAR **	ppwszAbsoluteRowURL	// [out]
	);
	
	HRESULT	GetOuterCollection
	(
		WCHAR *		pwszLeafURL,			// [in] the URL of the leaf row
		WCHAR **	ppwszOuterCollection	// [out] the URL of the outest Collection in the given path
	);

	HRESULT	GetCommonAncestor
	(
		WCHAR *		pwszFirstURL,		// [in] the first URL
		WCHAR *		pwszSecondURL,		// [in] the second URL
		WCHAR **	ppwszCommonURL	// [out] the URL of closest common ancestor
	);

	virtual BOOL MakeSuffix
	(
		DBCOUNTITEM ulNode,
		WCHAR *		pwszBaseURL, 
		WCHAR **	ppwszNewURL
	);

	virtual BOOL MakeSuffix
	(
		WCHAR *		pwszNode,
		WCHAR *		pwszBaseURL,
		WCHAR **	ppwszNewURL
	);

	virtual CSchema*	AddSchema
	(
		CSchema	*	pParentSchema,	// [in] pointer to the parent schema
		IRowset	*	pIParentRow,	// [in] interface to the current row
		WCHAR	*	pwszParentURL,	// [in] row URL of the parent
		LONG_PTR	lSeed			// [in] the seed value
	);
	
	WCHAR *	GetRootURL()
	{
		return m_pwszTreeRoot;
	}

	HRESULT	GetRowLevel
	(
		WCHAR *			pwszURL,	// [in] the URL of the chosen row
		DBCOUNTITEM	*	pulLevel	// [out] the level of the row in the hierarchy
	);

	LONG_PTR GetGenerateURL() 
	{
		return m_lGenerateURL;
	}

	// setting exact position inside a tree
	inline void	ResetPosition()
	{
		m_pCrtSchema		= m_pRootSchema;
		m_pStartingSchema	= m_pRootSchema;
	}

	HRESULT	SetPosition(WCHAR *pwszURL);

	void MoveDownToChildNode(DBCOUNTITEM ulChildOrdinal);

	// move to the next sibling (if there is any)
	HRESULT	MoveToNextSibling();

	// move to the next node 
	// this should set the position and return the URL of that node
	HRESULT	MoveToNextNode
	(
		WCHAR **	ppwszURL	// [out] the URL of the node
	);

	void ReturnFromChildNode();

	HRESULT	GetRow
	(
		DBCOUNTITEM	ulRow,		// [in] the number of row (as a leaf)
		WCHAR **	ppwszURL	//[out] the URL of the chosen row
	);

	// get the number of the first leaf in the subtree
	// starting at the current position
	DBCOUNTITEM GetNoOfFirstLeafInSubtree(CSchema*);

	// @cmember tests whether the row give by its URL is a collection
	BOOL IsCollection(WCHAR*);
	BOOL IsCollection(CSchema *pSchema);

	// methods that check the column values of a row
	HRESULT	VerifyRowValues
	(
		WCHAR *		pwszRowURL,			// [in] the URL of the row to be checked
		IUnknown *	pIUnk,				// [in] ptr to the row interface
		BOOL		fFromRowset = TRUE	// [in] whether it was obtained from a rowset 
	);

	HRESULT	VerifyRowValues
	(
		WCHAR *	pwszRowURL				// [in] the URL of the row to be checked
	);


	CSchema	* RemoveSchema(CSchema *pSchema);
	CSchema	* RemoveSchema(WCHAR	*pwszURL);	// [in] URL associated with the schema

};


#endif _CTree_HPP_